/*
 * Copyright © 2009 Perseus Project - Tufts University <http://www.perseus.tufts.edu>
 *
 * This file is part of TranscoderPerseus.
 *
 * TranscoderPerseus is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * TranscoderPerseus is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with TranscoderPerseus.  If not, see <http://www.gnu.org/licenses/>.
 */

package eu.himeros.transcoder;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * Static version of Transcoder.
 * @see Transcoder for documentation.
 *
 * @author federico.boschetti.73[AT]gmail.com
 */
public class Tcoder {

    private static Hashtable<String, String> directHt;
    private static Hashtable<String, String> reverseHt;
    private static Hashtable<String, String> ht;
    private static int maxLen = 0;
    private static Pattern pattern=null;
    private static boolean notInited=true; // not yet initialized

    /**
     *
     * @param reverse
     */
    public static void reverse(boolean reverse){
        if(reverse){
            ht=reverseHt;
        }else{
            ht=directHt;
        }
    }

    /**
     *
     */
    public static void reverse(){
        ht=reverseHt;
    }

    /**
     *
     * @param transFileName
     */
    public static void setTranscoder(String transFileName){
        try {
            /* read code file, transform utf8s in chars and put
            into a hashtable the sequences of char codes */
            String codeFrom;
            String codeTo;
            File transFile = new File(transFileName);
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(transFile), "UTF-8"));
            directHt = new Hashtable<String, String>();
            reverseHt = new Hashtable<String, String>();
            String line=null;
            String lineLeft = "";
            String lineRight = "";
            String[] items=null;
            while ((line = br.readLine()) != null) {
                if (!line.startsWith("//")) {
                    items=line.split("\t");
                    lineLeft=items[0];
                    lineRight=items[1];
                    if (lineLeft.contains("\\u")) {
                        codeFrom = utf8sToChars(lineLeft);
                    } else {
                        codeFrom = lineLeft;
                    }
                    if (codeFrom.length() > maxLen) {
                        maxLen = codeFrom.length();
                    }
                    if(lineRight.contains("\\u")){
                        codeTo = utf8sToChars(lineRight);
                    }else{
                        codeTo=lineRight;
                    }
                    directHt.put(codeFrom, codeTo);
                    reverseHt.put(codeTo, codeFrom);
                }
            }
            ht=directHt;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     *
     * @param transFileName
     * @param reverse
     */
    public static void setTranscoder(String transFileName, boolean reverse){
            setTranscoder(transFileName);
            reverse(reverse);
        }

    /**
     *
     * @param ht
     */
    public static void setTranscoder(Hashtable<String,String> ht){
        directHt=ht;
        makeReverseHt();
    }

    /**
     *
     * @return
     */
    public static Hashtable<String,String> getTranscoder(){
        return ht;
    }

    /**
     *
     * @return
     */
    public static Hashtable<String,String> getDirectTranscoder(){
        return directHt;
    }

    /**
     *
     * @return
     */
    public static Hashtable<String,String> getReverseTranscoder(){
        return reverseHt;
    }

    /**
     *
     * @return
     */
    public static String getCodes() {
        String res="";
        Set<String> keys=ht.keySet();
        String key="";
        for(Iterator<String> i=keys.iterator();i.hasNext();){
            key=i.next();
            res+=key+"\t"+ht.get(key)+"\n";
        }
        return res;
    }

    /**
     *
     * @param key
     * @return
     */
    public static String decode(String key){
        return ht.get(key);
    }

    /**
     *
     * @param str
     * @return
     */
    private static String utf8sToChars(String str) {
        if(str.equals("\\uxxxx")) return "";
        StringBuffer result =new StringBuffer();
        Pattern p=Pattern.compile("(?:\\\\)u([0-9A-Fa-f]{4})");
        Matcher m=p.matcher(str);
        while(m.find()){
            m.appendReplacement(result,""+((char)Long.parseLong(m.group(1),16)));
        }
        m.appendTail(result);
        return result.toString();
    }

    /**
     *
     * @param inStr
     * @return
     */
    public static String parse(String inStr){
        if(inStr==null||inStr.length()==0) return "";
        inStr += addSpaces(maxLen);
        String frag = "";
        String outStr = "";
        String code = "";
        int len = inStr.length();
        int iLeft = 0;
        int iRight = maxLen;
        while(iRight <= len){
            while(iRight > iLeft){
                frag = inStr.substring(iLeft, iRight);
                code = ht.get(frag);
                if (code != null) {
                    outStr += code;
                    break;
                }
                iRight--;
            }
            if(iRight==iLeft){
                iRight+=1;
                outStr+=frag;
            }
            iLeft = iRight;
            iRight += maxLen;
        }
        if(outStr.length()>1){
            return outStr.substring(0,outStr.length()-1);
        }else return outStr;
    }

    /**
     *
     * @param transFileName
     * @param reverseBool
     * @param inStr
     * @return
     */
    public static String parse(String transFileName, String reverseBool, String inStr){
        if(notInited){
            setTranscoder(transFileName, Boolean.parseBoolean(reverseBool));
            notInited=false;
        }
        return parse(inStr);
    }

    /**
     *
     * @param n
     * @return
     */
    private static String addSpaces(int n) {
        String res = "";
        for (int i = 0; i < n; i++) {
            res += " ";
        }
        return res;
    }

    /**
     *
     * @param inStr
     * @param regexp
     * @return
     */
    public static String parse(String inStr, String regexp){
        pattern=Pattern.compile(regexp);
        Matcher matcher=pattern.matcher(inStr);
        StringBuffer sb=new StringBuffer();
        while(matcher.find()){
            if(matcher.groupCount()==3){
                matcher.appendReplacement(sb, matcher.group(1)+parse(matcher.group(2)+matcher.group(3)));
            }else{
                matcher.appendReplacement(sb, parse(matcher.group(1)));
            }
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     *
     * @param transFileName
     * @param reverseBool
     * @param inStr
     * @param regexp
     * @return
     */
    public static String parse(String transFileName, String reverseBool, String inStr, String regexp){
        if(notInited){
            setTranscoder(transFileName, Boolean.parseBoolean(reverseBool));
            notInited=false;
        }
        return parse(inStr,regexp);
    }

    /**
     *
     * @param inStr
     * @param startTagRegexp
     * @param endTagRegexp
     * @param del
     * @return
     */
    public static String parse(String inStr, String startTagRegexp, String endTagRegexp, boolean del){
        if(!del){ //if del is true, tags that open and close the sequence are discarded
            startTagRegexp="("+startTagRegexp+")";
            endTagRegexp="("+endTagRegexp+")";
        }
        return parse(inStr, startTagRegexp+"(.*?)"+endTagRegexp);
    }

    /**
     *
     * @param transFileName
     * @param reverseBool
     * @param inStr
     * @param startTagRegexp
     * @param endTagRegexp
     * @param delBool
     * @return
     */
    public static String parse(String transFileName, String reverseBool, String inStr, String startTagRegexp, String endTagRegexp, String delBool){
        if(notInited){
            setTranscoder(transFileName, Boolean.parseBoolean(reverseBool));
            notInited=false;
        }
        return parse(inStr, startTagRegexp, endTagRegexp, Boolean.parseBoolean(delBool));
    }

    /**
     *
     */
    private static void makeReverseHt(){
        String val=null;
        String key=null;
        Enumeration<String> keys=directHt.keys();
        while(keys.hasMoreElements()){
            key=keys.nextElement();
            val=directHt.get(key);
            reverseHt.put(val, key);
        }
    }

    /**
     *
     */
    public static void printUsage(){
            System.out.println("usage: eu.himeros.transcoder.Tcoder.parse(<transFile> <reverseBool> <inStr>)");
    }
}